/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-09-14 10:47:48
 * @LastEditTime: 2021-10-09 10:02:27
 * @Description:  This files is for sdmmc user interface implementation
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 * 1.0   Zhugengyu   2021/8/6    init
 */
#include <string.h>
#include "ft_assert.h"
#include "parameters.h"
#include "fsleep.h"
#include "f_sdmmc_hw.h"
#include "f_sdmmc.h"

extern u32 FSdmmcReset(FSdmmcCtrl *ctrl_p);
extern void FSdmmcDisableIntr(FSdmmcCtrl *ctrl_p);

u32 FSdmmcInitialize(FSdmmcCtrl *ctrl_p, const FSdmmcConfig *config_p)
{
    FT_ASSERTZERONUM(ctrl_p && config_p);
    u32 reg_val;
    u32 ret = FSDMMC_SUCCESS;

    if (&ctrl_p->config != config_p)
        ctrl_p->config = *config_p;

    /* Disable card detection */
    FSDMMC_SET_SD_SEN(ctrl_p, 0x0);

    reg_val = FSDMMC_SD_SEN_REG_DEBNCE(FSDMMC_CLK_DIVIDER(FSDMMC_SEN_DEBNCE));
    FSDMMC_SET_SD_SEN(ctrl_p, reg_val);

    /* Configure to default cmd data timeout for 1s */
    FSDMMC_SET_CMD_TIMEOUT(ctrl_p, FSDMMC_CMD_TIMEOUT);
    FSDMMC_SET_DATA_TIMEOUT(ctrl_p, FSDMMC_DATA_TIMEOUT);

    /* handle DMA cache */
    FSDMMC_SET_HDS_AXI_CONF1(ctrl_p, 0x1001);
    FSDMMC_SET_HDS_AXI_CONF2(ctrl_p, 0x12221222);

    FSDMMC_SET_CTRL_SETTING(ctrl_p, FSDMMC_PERMDW_STD_END | FSDMMC_PERMDR_STD_END);

    ret = FSdmmcReset(ctrl_p);
    ret |= FSdmmcInitIntr(ctrl_p);

    if (FSDMMC_SUCCESS == ret)
        ctrl_p->is_ready = FT_COMPONENT_IS_READY;

    FSDMMC_INFO("sdmmc ctrl instance inited : 0x%x", ret);
    return FSDMMC_SUCCESS;
}

void FSdmmcDeInitialize(FSdmmcCtrl *ctrl_p)
{
    FT_ASSERTVOID(ctrl_p);
    FSdmmcDisableIntr(ctrl_p);
    memset(ctrl_p, 0, sizeof(*ctrl_p));
    return;
}

u32 FSdmmcSetCardClk(FSdmmcCtrl *ctrl_p, u32 clk_freq_khz)
{
    FT_ASSERTZERONUM(ctrl_p);
    u32 clk_hz = 1000 * clk_freq_khz;

    u32 div = FSDMMC_CLK_DIVIDER(clk_hz);
    u32 ret = FSDMMC_SUCCESS;

    FSDMMC_SET_CLK_DIV(ctrl_p, div & FSDMMC_CLK_DIV_MASK);
    FSDMMC_SET_SD_DRV(ctrl_p, FSDMMC_DEFAULT_DRV);
    FSDMMC_SET_SD_SAMP(ctrl_p, FSDMMC_DEFAULT_SAMP);    

    return FSdmmcReset(ctrl_p);
}

boolean FSdmmcDetectCard(FSdmmcCtrl *ctrl_p)
{
    FT_ASSERTZERONUM(ctrl_p);
    /* 检查卡检测管脚信号, 低电平表示卡在位 */
    if (FSDMMC_STATUS_REG_CDSL == (FSDMMC_STATUS_REG_CDSL & FSDMMC_GET_STATUS(ctrl_p)))
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

u32 FSdmmcWaitCmdEnd(FSdmmcCtrl *ctrl_p)
{
    FT_ASSERTZERONUM(ctrl_p);
    int retries = 0;

    u32 status_mask = FSDMMC_NORMAL_INT_STATUS_CC | FSDMMC_NORMAL_INT_STATUS_EI; /* 等待命令完成或者发生错误 */
    u32 status;

    do
    {
        status = FSDMMC_GET_NORMAL_INT_STATUS(ctrl_p) & status_mask;
        FSDMMC_DEBUG("status :0x%x", FSDMMC_GET_NORMAL_INT_STATUS(ctrl_p));
        fsleep_millisec(10);
    } while ((!status) && (FSDMMC_TIMEOUT > retries++));

    if (status & FSDMMC_NORMAL_INT_STATUS_EI)
    {
        FSDMMC_ERROR("wait cmd failed !!!");
        return FSDMMC_ERR_CMD_FAILED;
    }
    else if (FSDMMC_TIMEOUT <= retries)
    {
        FSDMMC_ERROR("wait cmd timeout!!!");
        return FSDMMC_ERR_TIMEOUT;
    }

    return FSDMMC_SUCCESS;   
}

u32 FSdmmcWaitDataEnd(FSdmmcCtrl *ctrl_p, boolean read)
{
    u32 status_mask;
    u32 status;
    int retries = 0;

    if (read)
        status_mask = FSDMMC_BD_ISR_REG_RESPE | FSDMMC_BD_ISR_REG_DAIS; /* 等待DMA传输完成或者发生错误 */
    else
        status_mask = FSDMMC_BD_ISR_REG_TRS | FSDMMC_BD_ISR_REG_DAIS; /* 等待DMA传输完成或者发生错误 */    

    /* 等待DMA传输完成 */
    do
    {
        status = FSDMMC_GET_BD_ISR_STATUS(ctrl_p) & status_mask;  
        fsleep_millisec(10);
    } while ((!status) && (FSDMMC_TIMEOUT > retries++));
    
    FSDMMC_DEBUG("BD_ISR_REG : 0x%08x\n", FSDMMC_GET_BD_ISR_STATUS(ctrl_p));
    FSDMMC_DEBUG("STATUS_REG : 0x%08x\n", FSDMMC_GET_STATUS(ctrl_p));    

    if (status & FSDMMC_BD_ISR_REG_DAIS)
    {
        FSDMMC_ERROR("BD Data error when %s blk!", read ? "read" : "write");
        return FSDMMC_ERR_DATA_FAILED;
    }
    else if (FSDMMC_TIMEOUT <= retries)
    {        
        FSDMMC_ERROR("BD Data timeout !!!");
        return FSDMMC_ERR_TIMEOUT;
    }    

    return FSDMMC_SUCCESS;    
}

/**
 * @name: FSdmmcPrintRespType
 * @msg: print response type for debug
 * @return {*}
 * @param {u32} hw_cmd
 */
static const char* FSdmmcPrintRespType(u32 hw_cmd)
{
    const char *str;

    switch (FSDMMC_CMD_RESP_MASK & hw_cmd)
    {
    case FSDMMC_CMD_NO_RESP:
        str = "NONE";
        break;
    case FSDMMC_CMD_RESP_136_BIT:
        str = "LONG";
        break;
    case FSDMMC_CMD_RESP_48_BIT:
        str = "SHORT";
        break;
    case FSDMMC_CMD_RESP_48_BIT_BUSY_CHECK:
        str = "SHORT CHECK BUSY";
        break;
    default:
        FT_ASSERTNONERETURN(0);
    }

    return str;
}

void FSdmmcStartCmd(FSdmmcCtrl *ctrl_p, u32 hw_cmd, u32 arg)
{
    /* 清除状态寄存器 */
    FSDMMC_CLR_NORMAL_INT_STATUS(ctrl_p);
    FSDMMC_CLR_ERROR_INT_STATUS(ctrl_p);
    FSDMMC_CLR_BD_ISR_STATUS(ctrl_p);

    /* 设置命令 */
    FSDMMC_SET_CMD(ctrl_p, hw_cmd);

    /* 设置参数，同时触发发送命令 */
    FSDMMC_SET_CMD_ARG(ctrl_p, FSDMMC_CMD_ARG_MASK & arg);    

    FSDMMC_INFO("CMD: 0x%08x ", FSDMMC_GET_CMD(ctrl_p));
    FSDMMC_INFO("ARG: 0x%08x", FSDMMC_GET_CMD_ARG(ctrl_p));
    FSDMMC_INFO("RESP: %s", FSdmmcPrintRespType(hw_cmd));    
}